home *** CD-ROM | disk | FTP | other *** search
- /* audiotools2.c */
-
- /* audiotools.h built in here so that audiotools is a package deal */
-
- #define StartChannel(c) ControlChannel(c, CMD_START)
- #define StopChannel(c) ControlChannel(c, CMD_STOP)
- #define ResetChannel(c) ControlChannel(c, CMD_RESET)
- #define FlushChannel(c) ControlChannel(c, CMD_FLUSH)
-
- #define BIG_WAVE 256L /* size of biggest waveform */
- #define NBR_WAVES 7L /* number of waves per instrument */
- #define WAVES_TOTAL 1024L /* alloc size for instrument's waves */
-
- #define DEFAULT_DURATION 500L /* 500/1000ths of a second default */
- #define AUDBUFFERS 20L /* iob msg packets before need to allot */
- #define YES 1L
- #define NO 0L
-
- #define BAD_CHANNEL_SELECTED -1L /* channel # out of range */
- #define NOT_YOUR_CHANNEL -2L /* not owned by your task */
- #define OUT_OF_RANGE_FREQ -3L /* frequency that we cannot play */
- #define OUT_OF_RANGE_PRI -4L /* priority value wont fit in 1 byte */
-
- /* REDEFINITION from article - now contains one new field at the bottom
- * of the data structure.
- */
- struct ExtIOB {
- struct IORequest ioa_Request;
- WORD ioa_AllocKey;
- UBYTE *ioa_Data;
- ULONG ioa_Length;
- UWORD ioa_Period;
- UWORD ioa_Volume;
- UWORD ioa_Cycles;
- struct Message ioa_WriteMsg; /* up to here, same as IOAudio */
- LONG iob_Identifier; /* This field is added */
- };
-
- /* a few forward declarations */
-
- extern struct ExtIOB *GetIOB();
- extern int FreeIOB();
- extern int GetChannel();
- extern int GetStereoPair();
- extern int InitBlock();
- extern struct MsgPort *CreatePort();
-
- extern APTR AllocMem();
- extern struct Message *GetMsg();
- extern struct Task *FindTask();
-
- struct auMsg {
- struct Message au_Message;
- LONG aum_Identifier; /* matches the bottom of ExtIOB */
- };
-
- /* Note that the definition of ExtIOB has now been changed (added to) */
-
- /* forward declaration */
- extern struct MsgPort *InitAudio();
-
- /* these are used to keep track of allocated channels */
-
- struct Unit *unit[4]; /* global pointers to Units */
- WORD key[4]; /* global value for alloc keys */
- struct Task *usertask[4]; /* in preparation for making this
- * a shared library of routines
- * (loadable from disk), keep track
- * of which user actually owns a
- * channel currently.
- */
-
- struct IOAudio openIOB; /* IOB to open and close the device */
- struct MsgPort *auReplyPort; /* temporary pointer */
- struct MsgPort *controlPort; /* Port for ControlChannel functions */
-
- char *globalname = "global"; /* the name for global IOB's */
- char *dynamicname = "dynamic"; /* the name for dynamic IOB's */
-
- UBYTE stereostuff[4] = { 3, 5, 10, 12 }; /* channel masks for stereo */
- UBYTE anychan[4] = { 1, 2, 4, 8 }; /* channel masks for mono */
-
- /* Resolve most all externals */
-
- struct ExtIOB audbuffer[AUDBUFFERS]; /* globals to build-in */
- UBYTE *chipaudio[4]; /* pointers to waveforms in CHIP RAM */
- struct Device *device; /* global pointer to audio device */
- LONG datalength[4]; /* length of the data for a wave */
- struct MsgPort *replyPort[4]; /* one ReplyPort per channel */
- BYTE inuse[AUDBUFFERS]; /* keep track of globals in-use */
- LONG dynamix[4]; /* counters for how many
- * dynamically allocated audio
- * message I/O blocks
- */
-
-
- /* Each waveform buffer contains 8 octaves of the wave.
- * The offset values specify where in the buffer the
- * proper waveform table for that octave begins.
- */
- int woffsets[] = { 0, 256, 384, 448, 480, 496, 504, 508, 510 };
-
- /* Length of each waveform within a buffer */
- int wlen[] = { 256, 128, 64, 32, 16, 8, 4, 2, 1 };
-
- /* Period value to go with particular notes within an octave. */
-
- int perval[] = { 428, 404, 381, 360, 339, 320,
- 302, 285, 269, 254, 240, 226, 214 };
-
- UBYTE *w1, *w2, *w3;
- BYTE *owptr[4] = { NULL, NULL, NULL, NULL };
-
- /* InitAudio is different from that published in the article -
-
- InitAudio now returns a value, an address of a message port at which
- your task receives a message when a particular note BEGINS to play.
- You must save this value somewhere, and use it to call MayGetNote
- or FinishAudio. MayGetNote is the name of the routine that you call
- to check if a note has begun to play.
-
- InitAudio also has been modified to return to its caller when there
- are problems, rather than calling finishup routines itself. This is better
- programming practice.
-
- */
-
- struct MsgPort *
- InitAudio()
- {
- LONG error,i;
- struct MsgPort *userport; /* NEW item */
-
- LONG firstuser; /* THIS WILL GET MOVED when shared library is made */
- firstuser = TRUE;
-
- /* Declare all message blocks available */
- for(i=0; i<AUDBUFFERS; i++) { inuse[i] = NO; }
-
- /* Open device but don't allocate channels */
- openIOB.ioa_Length = 0; /* (no allocation table) */
-
- error = OpenDevice("audio.device",0,&openIOB,0);
- if(error) return((struct MsgPort *)0);
-
- /* Get the device address for later use */
- device = openIOB.ioa_Request.io_Device;
-
- /* Create ports for replies from each channel as well as
- * one port to be used for the control and synchonous functions */
-
- for(i=0; i<4; i++)
- {
- auReplyPort = CreatePort(0,0);
- replyPort[i] = auReplyPort;
- if(auReplyPort == 0) return((struct MsgPort *)0);
- chipaudio[i] = 0; /* have not yet created the waves */
-
- datalength[i] = 1; /* might use for custom sound samples */
-
- /* Also, zero out key values for each channel, as well as
- * unit value and usertask value (no channel owned by any task)
- */
-
- /* When implemented as a shared library, "firstuser" will only
- * be true when the library is first opened.
- */
- if(firstuser)
- {
- key[i] = 0;
- unit[i] = 0;
- usertask[i] = 0;
- }
- }
- controlPort = CreatePort(0,0);
- if(controlPort == 0) return((struct MsgPort *)0);
-
- error = MakeWaves();
- if(error == -1) return((struct MsgPort *)0);
-
- for(i=0; i<4; i++)
- { dynamix[i] = 0; } /* no dynamic I/O blocks allocated
- * for any channel thus far */
-
- userport = CreatePort(0,0);
-
- return(userport);
- }
-
- int
- CheckIOBDone()
- {
- LONG i, status;
-
- status = 0; /* means there are still some iob's in play */
- /* when status = 5, then everything is free */
-
- for(i=0; i<AUDBUFFERS; i++)
- { if(inuse[i] == YES)
- {
- /* Sooner or later, this will catch both
- * the statics and dynamics. Note that
- * this will only work if NO (REPEAT: NO)
- * iob's sent off with a duration value
- * of "0", because zero means "forever"
- */
- ReEmployIOB();
- }
- }
- /* Note to implementors... maintaining inuse[i] now seems
- * like a lousy idea, unless it is accompanied by a variable
- * statics_inplay that decrements to zero when all statics
- * are done. That makes it much easier to check than going
- * through all of the inuse[]'s.
- */
-
- for(i=0; i<4; i++)
- {
- if(dynamix[i] > 0)
- /* If this channel still playing a */
- /* dynamically allocated block, wait */
- /* for all messages to return before */
- /* the program exits. */
- {
- ReEmployIOB(); /* take another shot at freeing it all */
- }
- }
- for(i=0; i<4; i++) /* Check again as we nearly exit */
- {
- if(dynamix[i] == 0) status++;
- }
- if(status == 4) /* All dynamics are free, now check
- * the statics. Any not free force
- * an early return. */
- {
- #ifdef DEBUG
- printf("ch0,1,2,3 have %ld,%ld,%ld,%ld dyn.blocks playing\n",
- dynamix[0], dynamix[1], dynamix[2], dynamix[3]);
- #endif DEBUG
- for(i=0; i<AUDBUFFERS; i++)
- {
- if(inuse[i] == YES)
- {
- #ifdef DEBUG
- printf("iob still in use is: %ld\n",i);
- #endif DEBUG
- return(0);
- }
- }
- printf("All global I/O blocks are done\n");
-
- return(1); /* DONE! */
- }
- else
- {
- return(0); /* still some out there! */
- }
- }
-
-
- FinishAudio(uport)
- struct MsgPort *uport;
- {
- LONG i;
- struct auMsg *aum; /* A little bigger than a standard
- * message, but this routine will
- * not really know (or care) about
- * the difference.
- */
- if(uport == 0)
- {
- goto no_init; /* InitAudio didn't work, bypass
- * the usual port emptying and so on.
- */
- }
- /* If the user says FinishAudio, IT MEANS FINISH AUDIO.
- * Flush anything that is still in play, NOW. You can
- * use "CheckIOBDone()" to see if everything is finished
- * BEFORE you call FinishAudio. If CheckIOBDone() is
- * equal to zero (FALSE), it means that something is still
- * playing.
- */
- for(i=0; i<4; i++) FlushChannel(i);
-
- while(CheckIOBDone() == 0)
- {
- Delay(12); /* Be a good multitasking neighbor;
- * sleep a little before trying again */
- }
- /* Empty the port if the user has not yet done so */
- while((aum = (struct auMsg *)GetMsg(uport)) != NULL)
- {
- aum->au_Message.mn_ReplyPort = 0; /* let system deallocate it */
- }
- ReEmployIOB(); /* free all static and dynamic messages */
-
- for(i=0; i<4; i++) FreeChannel(i);
-
- DeletePort(uport);
-
- no_init:
- if(device) CloseDevice(&openIOB);
- printf("closed the device\n");
-
- for(i=0; i<4; i++)
- {
- if(chipaudio[i]) FreeMem(chipaudio[i],WAVES_TOTAL);
- if(replyPort[i])
- DeletePort(replyPort[i]);
- }
- if(controlPort) DeletePort(controlPort);
- return(0); /* no errors */
- }
-
-
- int
- ControlChannel(channel, command)
- WORD channel;
- WORD command;
- {
- LONG rtn;
- struct ExtIOB *iob, controlIOB;
-
- if(channel < 0 || channel > 3) return(BAD_CHANNEL_SELECTED);
- if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL);
-
- iob = &controlIOB;
- iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
-
- InitBlock(iob,channel); /* init it for CMD_WRITE, then change */
-
- iob->ioa_Request.io_Command = command;
- iob->ioa_Request.io_Flags = IOF_QUICK;
-
- BeginIO(iob);
- WaitIO(iob);
- rtn = ((LONG)(iob->ioa_Request.io_Error));
- return(rtn);
- }
-
- struct ExtIOB *
- GetIOB(ch)
- LONG ch;
- {
- WORD i,use_reply;
- struct ExtIOB *iob; /* in case we need to allocate one */
- ReEmployIOB(); /* find already used ones and free them */
- /* so that when we do a get... */
- if(ch == -1) use_reply = 0; /* which reply port to use */
- else use_reply = ch;
-
- for(i=0; i<AUDBUFFERS; i++)
- {
- if(inuse[i] == NO)
- {
- inuse[i] = YES;
-
- audbuffer[i].ioa_Request.io_Device = device;
- audbuffer[i].ioa_Request.io_Message.mn_ReplyPort =
- replyPort[use_reply];
- audbuffer[i].ioa_Request.io_Message.mn_Length = i;
- audbuffer[i].ioa_Request.io_Message.mn_Node.ln_Name =
- globalname;
- #ifdef DEBUG
- printf("Using global iob\n");
- #endif DEBUG
- return(&audbuffer[i]);
- }
- }
- /* if all globals are in use, have to allocate one */
- iob = (struct ExtIOB *)AllocMem(sizeof(struct ExtIOB),
- MEMF_CLEAR );
- /* FIXED A BUG HERE, THIS ^^^^^^ was MEMF_FAST */
-
- if(iob == 0) return(0); /* out of memory */
- else
- {
- iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort =
- replyPort[use_reply];
- iob->ioa_Request.io_Message.mn_Node.ln_Name =
- dynamicname;
- iob->ioa_Request.io_Message.mn_Length = dynamix[use_reply];
- dynamix[use_reply] += 1; /* add one to number allocated
- * for a specific channel */
- #ifdef DEBUG
- printf("Using dynamic iob\n");
- #endif DEBUG
- return(iob);
- }
- return(0);
- }
-
-
- /* Free a global or an allocated IOB */
- int
- FreeIOB(iob, ch)
- struct ExtIOB *iob;
- LONG ch; /* which channel was it attached to? */
- {
- WORD i;
-
- if(iob->ioa_Request.io_Message.mn_Node.ln_Name == dynamicname)
- {
- FreeMem(iob, sizeof(struct ExtIOB));
- if(dynamix[ch]) dynamix[ch] -= 1; /* subtract one if nonzero */
- return(0L);
- }
- else if(iob->ioa_Request.io_Message.mn_Node.ln_Name == globalname)
- {
- i = iob->ioa_Request.io_Message.mn_Length;
-
- if(i < AUDBUFFERS)
- {
- inuse[i] = NO; /* frees this one for reuse */
- }
- return(0L);
- }
- /* if get here, the names don't match... something is wrong.*/
- else
- {
- printf("FreeIOB: names don't match...unknown error\n");
- return(-1); /* unknown source of IOB fed to routine. */
- }
- return(0);
- }
-
- /* Initialize an audio I/O block for default CMD_WRITE operation. */
- int
- InitBlock(iob, channel)
- struct ExtIOB *iob;
- WORD channel;
- {
- /* Device and ReplyPort fields have been initialized by GetIOB */
- iob->ioa_Request.io_Unit = unit[channel];
-
- /* Allocation key */
- iob->ioa_AllocKey = key[channel];
-
- /* Where is the waveform? Just be sure is in MEMF_CHIP!!! */
-
- /* USER initializes datalength[ch] before calling this; */
- /* for sampled sound command write operation. */
- iob->ioa_Data = chipaudio[channel];
- iob->ioa_Length = datalength[channel];
-
- /* Another routine, must initialize:
-
- period ioa_Period
- volume ioa_Volume
- cycles ioa_Cycles
- message ioa_WriteMsg
- */
- /* Default command type is CMD_WRITE */
- iob->ioa_Request.io_Command = CMD_WRITE;
-
- /* If IOF_QUICK is zeroed, this would affect the
- * period and volume. If a CMD_WRITE, it queues if
- * another note is already playing. We queue CMD_WRITES.
- */
- iob->ioa_Request.io_Flags = ADIOF_PERVOL;
- return(0);
- }
-
-
- /* To request "any" channel, use ch = -1;
- * To request a specific channel, use ch = {0, 1, 2 or 3};
- * Again NOTE, this returns two globals as well as the channel number!
- */
-
- int
- GetChannel(ch)
- LONG ch;
- {
- int error, value;
- struct ExtIOB *iob, controlIOB;
-
- iob = &controlIOB;
- iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
-
- InitBlock(iob,0); /* init it for CMD_WRITE, then change */
-
- iob->ioa_Request.io_Message.mn_Node.ln_Pri = 20;
- iob->ioa_Request.io_Command = ADCMD_ALLOCATE;
-
- if(ch == -1)
- {
- iob->ioa_Data = (UBYTE *)anychan;
- iob->ioa_Length = 4;
- }
- else if(ch >=0 && ch <= 3)
- {
- /* Test to be sure that this channel is now free. If
- * usertask[i] is not zero, either the current task
- * has already allocated this channel, or a different
- * task is now using it.
- */
-
- /* NOTE ***** ENHANCEMENT COMING HERE ***** */
-
- if(usertask[ch] != 0) return(-1);
-
- /* Enhancement might be: look at the running priority
- * of the current task as compared to the running priority
- * of the task in usertask[i]. If not same task and if
- * the current task has a higher priority, STEAL the channel!
- * Alternative (seems better) is to have a global variable
- * called audPriority to be set by a new function SetAudPriority
- * (for a given task only), and that global priority value
- * would be used for GetChannel and LockChannel requests.
- */
- iob->ioa_Data = (UBYTE *)(&anychan[ch]);
- iob->ioa_Length = 1;
- }
- else /* chose a bad channel number; cannot allocate it */
- {
- return(-1);
- }
- iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
- BeginIO(iob);
- error = WaitIO(iob); /* returns nonzero if error */
- if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
- {
- GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
- }
- if(error)
- {
- return(-1);
- }
- switch((LONG)(iob->ioa_Request.io_Unit))
- {
- case 1: value = 0; break;
- case 2: value = 1; break;
- case 4: value = 2; break;
- case 8: value = 3; break;
- default: value = -1; break;
- }
- if(value == -1) return(-1L);
-
- unit[value] = (iob->ioa_Request.io_Unit);
- key[value] = (iob->ioa_AllocKey);
- usertask[value] = FindTask(0); /* THIS user task owns it now */
-
- return(value);
- }
-
- int
- FreeChannel(channel)
- LONG channel;
- {
- int error;
- struct ExtIOB *iob, controlIOB;
-
- if(channel < 0 || channel > 3) return(BAD_CHANNEL_SELECTED);
- if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL);
-
- iob = &controlIOB;
- iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
-
- InitBlock(iob,channel); /* init it for CMD_WRITE, then change it */
- /* (pick up unit, key value for channel) */
- iob->ioa_Request.io_Command = ADCMD_FREE;
- iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
- BeginIO(iob);
- error = WaitIO(iob); /* returns nonzero if error */
-
- /* Educational note - the docs indicate that audio, even though
- * told to do an io item as "quickio", can, if it chooses, not do
- * it quick. In that case, the reply is queued to the
- * reply port and it has to be removed. That is why
- * the following status check has been installed here.
- * (Actually this check probably can be removed because
- * WaitIO, just above, removes the message from the port
- * if it does arrive there after all.)
- */
- if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
- {
- GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
- }
- usertask[channel] = 0; /* free again... */
- if(error)
- {
- return(error);
- }
- return(0);
- }
-
- /* SOME OF THE FOLLOWING ROUTINES ARE PARAPHRASED FROM A USENET and BIX
- * POSTING MADE IN 1985 BY STEVEN A. BENNETT.
- */
- /* I have modified his routines to queue the audio commands in
- * place of starting forever-duration and canceling each note.
- * Many of his original comments have been incorporated into
- * the article.
- */
-
- /* PlayNote(...) */
-
- /* ******************************************************************* */
- /* NOTE: There are some differences in PlayNote as compared to the article.
- * See the audiotools.DOC for details.
- */
- /* ******************************************************************* */
-
- /* Starts a sound on the channel with specified period and volume. */
- /* This nice little routine takes a note and plays it on the given
- * voice. The note is basically an integer from
- * 0 to 11 (c to b) plus 12 per octave above the first and lowest.
- *
- * The waveform to use is determined by adding an index (woffsets[])
- * dependant on the octave.
- *
- * The length of the waveform (in wlen[]) is likewise dependant on
- * the octave. Note that octaves start with zero, not one.
- */
- int
- PlayNote(channel, note, wf, vol, duration, priority, messageport, id)
- char *wf; /* waveform to use */
- LONG vol, channel, duration, note; /* specific note number */
- LONG priority;
- struct MsgPort *messageport;
- LONG id;
- {
- LONG per, len, oct; /* period, length of waveform, which octave */
- char *wavepointer; /* where to find start of waveform */
- struct ExtIOB *iob;
- int frequency;
-
- if(channel < 0 || channel > 3) return(BAD_CHANNEL_SELECTED);
- if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL);
- if(note < 0 || note > 95) return(OUT_OF_RANGE_FREQ);
- if(priority < -128 || priority > 127)return(OUT_OF_RANGE_PRI);
-
- iob = GetIOB(channel);
-
- if(iob != 0)
- {
- InitBlock(iob, channel); /* set up for CMD_WRITE */
-
- oct = note / 12;
- wavepointer = wf + woffsets[oct];
- len = wlen[oct];
- per = perval[note % 12];
-
- /* Set the parameters */
- iob->ioa_Data = (UBYTE *)wavepointer;
- iob->ioa_Length = len;
- iob->ioa_Period = per;
- iob->ioa_Volume = vol;
-
- iob->ioa_Request.io_Message.mn_Node.ln_Pri = priority;
-
- /* additions for support of tell-me-when-note-starts */
-
- iob->iob_Identifier = id;
-
- /* Initialize message port. If 0, then no pushing back
- * of a message. If nonzero, message gets recirculated
- * by ReEmployIOB until the user finally acknowledges it
- * by using MayGetNote.
- */
- iob->ioa_WriteMsg.mn_ReplyPort = messageport;
-
- if(messageport != 0)
- {
- /* If 0, no sending message when note plays;
- * if nonzero, user gets a message that can
- * be read by MayGetNote. uport, received from
- * InitAudio, is where the message goes */
-
- /* Tell the audio device to "reply" to this message */
- iob->ioa_Request.io_Flags |= ADIOF_WRITEMESSAGE;
- }
-
- /* Look at the frequency that it is to play by backwards calc. */
- frequency = 3579545 / (len * per);
-
- /* Calculate cycles from duration in 1000ths of a second */
- /* Multiply all-in-one to maintain max precision possible */
- /* (all integer arithmetic.) */
-
- iob->ioa_Cycles = ((LONG)(frequency * duration)/1000);
- BeginIO(iob);
- return(0); /* all went ok */
- }
- return(-1); /* (else-part) iob was zero, couldn't do the above. */
- }
-
-
- /* SetPV(channel, per, vol)
- * int channel, per, vol;
- */
- int
- SetPV(channel, per, vol)
- int channel, per, vol;
- {
- int error;
- struct ExtIOB *iob, controlIOB;
-
- if(channel < 0 || channel > 3) return(BAD_CHANNEL_SELECTED);
- if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL);
-
- iob = &controlIOB;
- iob->ioa_Request.io_Device = device;
- iob->ioa_Request.io_Message.mn_ReplyPort = controlPort;
-
- InitBlock(iob, channel); /* set up for CMD_WRITE */
-
- iob->ioa_Period = per;
- iob->ioa_Volume = vol;
- iob->ioa_Request.io_Command = ADCMD_PERVOL;
- iob->ioa_Request.io_Flags = IOF_QUICK | ADIOF_PERVOL;
- BeginIO(iob); /* This one will be synchronous; affects whatever
- * is playing on this channel at this time.
- */
- error = WaitIO(iob); /* OK to wait, since it will return */
- return(error); /* copy of io_Error field; should be 0 */
- }
-
- /* SetWaves(w1, w2, w3): create first sawtooth, triangle and square wave */
-
- SetWaves(w1, w2, w3)
- UBYTE *w1, *w2, *w3;
- {
- int i, increment, value, sqvalue;
- value = 0; increment = 2;
- sqvalue = 127;
-
- for (i = 0; i < BIG_WAVE; ++i)
- {
- w1[i] = i; /* do the sawtooth */
-
- if(i > 62 && i < 180) increment = -2;
- else
- if(i >= 180) increment = 2;
-
- w2[i] = value; value += increment; /* triangle wave */
-
- if(i > 126) sqvalue = -127;
-
- w3[i] = sqvalue;
- }
- return(0);
- }
-
- /* ExpandWave(wfp) - replicate waves in decreasing sample sizes
- * BYTE *wfp;
- */
-
- ExpandWave(wfp)
- BYTE *wfp;
- {
- int i, j, rate;
- BYTE *tptr;
-
- rate = 1;
- tptr = wfp + BIG_WAVE;
- for (i = 0; i < NBR_WAVES - 1; ++i)
- {
- rate *= 2;
- for (j = 0; j < BIG_WAVE; j += rate)
- *tptr++ = wfp[j];
- }
- return(0);
- }
-
- /* MakeWaves()
- *
- * Just makes a sawtooth, triangle and square wave in chip mem
- * and expands them.
- */
- int
- MakeWaves()
- {
- /* allocate the memory for the waveforms.
- */
- w1 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
- w2 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
- w3 = (UBYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
-
- if (w1 == NULL || w2 == NULL || w3 == NULL)
- return(-1); /* ran out of memory! */
-
- /* get and expand the waveforms */
-
- SetWaves(w1, w2, w3);
- ExpandWave(w1); chipaudio[0]=w1;
- ExpandWave(w2); chipaudio[1]=w2;
- ExpandWave(w3); chipaudio[2]=w3;
- return(0);
- }
-
- /* =================================================================== */
- /* Here are NEW routines that did not appear in the AmigaWorld article.
- * These are in addition to the changes noted above for InitAudio and
- * FinishAudio, and changes in main() to accommodate them.
- */
-
- /* PlayFreq in this version is for INTEGER values of frequency only.
- *
- * To be more precise, you'll have to convert the integer calculations
- * to floating point. Almost identical to PlayNote, however, had to
- * add some error checking to the parameters AHEAD OF GetIOB because
- * if the frequency is out of range of what we have in our wave tables
- * currently, we have to reject the command. If it got rejected
- * after Getting an IOB, then we'd have to free it.
- */
- int
- PlayFreq(channel, freq, wf, vol, duration, priority, messageport, id)
- char *wf; /* waveform to use */
- LONG vol, channel, duration, freq; /* specific integer frequency */
- LONG priority;
- struct MsgPort *messageport;
- LONG id;
- {
- LONG per, len, oct; /* period, length of waveform, which octave */
- char *wavepointer; /* where to find start of waveform */
- struct ExtIOB *iob;
- LONG i, isave, accept;
-
- if(channel < 0 || channel > 3) return(BAD_CHANNEL_SELECTED);
- if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL);
- if(priority < -128 || priority > 127)return(OUT_OF_RANGE_PRI);
-
- /* see if we CAN represent this frequency, if not, reject it */
-
- for(i=0; i<9; i++)
- {
- /* Now figure out which waveform to use...
- * start with the first wlen value because
- * we want to use the longest waveform we can.
- */
- isave = i;
-
- accept = FALSE;
-
- per = 3579545 / (freq * wlen[i]);
-
- if(per > 127 && per < 500)
- {
- accept = TRUE;
- break;
- }
- }
- if(accept != TRUE) return(OUT_OF_RANGE_FREQ);
-
- iob = GetIOB(channel);
-
- if(iob != 0)
- {
- InitBlock(iob, channel); /* set up for CMD_WRITE */
-
- oct = isave; /* show the octave we are in */
- wavepointer = wf + woffsets[oct]; /* point to longest wave */
- len = wlen[oct]; /* show its length */
-
- /* Set the parameters */
- iob->ioa_Data = (UBYTE *)wavepointer;
- iob->ioa_Length = len;
- iob->ioa_Period = per;
- iob->ioa_Volume = vol;
-
- iob->ioa_Request.io_Message.mn_Node.ln_Pri = priority;
-
- /* additions for support of tell-me-when-note-starts */
- iob->iob_Identifier = id;
-
- /* see note on same line for PlayNote above */
- iob->ioa_WriteMsg.mn_ReplyPort = messageport;
-
- if(messageport != 0)
- {
- /* If 0, no sending message when note plays;
- * if nonzero, user gets a message that can
- * be read by MayGetNote. uport, received from
- * InitAudio, is what should be the value of
- * messageport.
- */
-
- /* Tell the audio device to "reply" to this message */
- iob->ioa_Request.io_Flags |= ADIOF_WRITEMESSAGE;
- }
-
- /* Calculate cycles from duration in 1000ths of a second */
- /* Multiply all-in-one to maintain max precision possible */
- /* (all integer arithmetic.) */
-
- iob->ioa_Cycles = ((LONG)(freq * duration)/1000);
- BeginIO(iob);
- return(0); /* all went ok */
- }
- return(-1L); /* (else-part) iob value was zero, out of memory probably */
- }
-
-
- /* Another new routine called MayGetNote. Its syntax is:
- *
- * note = MayGetNote(uport, flag)
- *
- * where uport is the address of the port you received from InitAudio.
- *
- * flag is a value of 0 or 1.
- *
- * when flag = 0, the routine returns immediately, with
- * a note value of 0 (no note available), or
- * the value of the first note notification
- * to arrive at the port.
- *
- * when flag = nonzero, the routine will wait if (and only if)
- * there is no note to be had. In other words,
- * you can cause your task to go to sleep until
- * the next note begins to play. You decide
- * what to do for a specific note value.
- *
- * note is the value you placed into the PlayNote or PlayFreq
- * identifier field. A report of this type
- * is ONLY generated when "messageport" is nonzero.
- *
- * CAUTION - if there are no more notes with messageport nonzero in
- * the queue and you specify TRUE for the flag, you can cause your
- * task to sleep forever!!
- */
-
- LONG
- MayGetNote(uport, flag)
- struct MsgPort *uport;
- LONG flag;
- {
- struct auMsg *aum;
- LONG wakemask;
-
- grabmessage:
- aum = (struct auMsg *)GetMsg(uport); /* is a message there? */
-
- if(aum) /* There was a message! */
- {
- /* The user has seen this msg, so the system can deallocate
- * the iob in which it occurs anytime in the future.
- * Now that we have received the message at our own reply
- * port, it belongs to us and we can do whatever we want
- * to it. Set the reply port value to zero now, as a signal
- * to FreeIOB that it can really do that!
- */
- aum->au_Message.mn_ReplyPort = 0;
-
- return(aum->aum_Identifier); /* return the value */
- }
- if(flag) /* If nonzero, we cause caller to sleep while
- * waiting for any identified iob to appear.
- */
- {
- wakemask = WaitPort(uport); /* not using wakemask */
- flag = 0;
- goto grabmessage; /* a goto! aaarrrggghhh! */
- }
- return(0L);
- }
-
- /* ReEmployIOB is not new (as compared to article) but it
- * has changed a lot since the article was written.
- */
-
- /* ReEmployIOB - look at ALL of the reply ports and if any IOBs
- * hanging around with nothing to do, free them.
- *
- * In the article, this routine removed iob's from the
- * reply ports via the usual GetMsg function. This
- * was not practical in this case when adding the support
- * for audio messages from the device.
- *
- * Problem was this:
- * Audio may still be playing the waveform as we get a
- * message through MayGetNote. MayGetNote marks the
- * iob message block as free-able, (when it finds that
- * the identifier field is set to zero) but we have to have a
- * way of recirculating in this list of messages.
- *
- * In other words, if something is free-able, free it,
- * otherwise leave it on the list. So rather than
- * removing things from the front of the list, lets just
- * walk through the message list, Remove (dequeue) what is
- * freeable and leave the rest there to look at the next time.
- */
-
- ReEmployIOB()
- {
- LONG i;
- struct MsgPort *mp;
- struct ExtIOB *iob;
-
- /* now declare a pointer to "the first iob pushed back onto the port" */
-
- struct ExtIOB *pushback;
-
- /* What happens here is that iob's are removed from the message port
- * when they come back from the audio device. If YOU have set the
- * messageport nonzero, it means that you wanted to know when
- * this note began to play. The WriteMsg part of the iob is then
- * linked, as a message, onto your user port. So this routine here
- * cannot free the iob until it is certain that YOU have finished
- * using it. The iob_Priority field is READ here. If it still
- * nonzero, the iob is pushed back onto the message port (on the
- * end of the message queue) to be read again. We hold a pointer
- * named pushback that lets us keep track of when we see that
- * again. If we see it twice, it means we have completed a full
- * circle through the queue of messages and have freed everything
- * that we can this time. Therefore, we examine it and either
- * free it or push it back again, then exit.
- */
-
- /* Tiny drawback to the pushback that we do is that there can
- * be more than one dynamic IOB with the same identifier value.
- * The id value is not used anywhere, execept for the debug
- * reporting, so it is not critical. Note that this is not
- * a problem with a pushback of a static iob because the inuse[]
- * array is being maintained for the statics.
- */
- for(i=0; i<4; i++) /* remove all iob's from ALL ports */
- /* (that is, unless we have to push one back) */
- {
- mp = replyPort[i];
-
- pushback = (struct ExtIOB *)0; /* nothing pushed back so far */
-
- while((iob = (struct ExtIOB *)GetMsg(mp)) != 0)
- {
- /* Here is what triggers the Identifier message for MayGetNote */
-
- /* First see if messageport in WriteMsg is greater than zero;
- * if so, audio device is done, but user hasnt acknowledged
- * this message yet (by using MayGetNote).
- */
- if(iob->ioa_WriteMsg.mn_ReplyPort != 0)
- {
- /* If we get here, we already know we will push it back */
- /* because User has not yet freed it (using MayGetNote) */
-
- PutMsg(mp, iob);
-
- if(iob == pushback) /* If so, we saw it already */
- {
- break; /* Go out to empty next port */
- }
- if(pushback == 0)
- {
- pushback = iob; /* Remember FIRST one pushed back */
- }
- }
- else /* messageport value is zero, can free the iob */
- {
- #ifdef DEBUG
- printf("freeing %ls iob; ID=%ld\n",
- iob->ioa_Request.io_Message.mn_Node.ln_Name,
- iob->ioa_Request.io_Message.mn_Length);
- #endif DEBUG
- FreeIOB(iob,i);
- }
- }
-
- }
- return(0);
- }
-
- /* Use this next function to determine if you (still) own a particular channel.
- * The audio device has an arrangement by which a higher priority request
- * for a channel than the one that already owns it can be made. The higher
- * priority request can actually cause a channel to be stolen from a user.
- * This feature may be implemented in a future version of audiotools,
- * (shared library version), in which, depending on the task's running
- * priority itself, a higher priority task could succeed at GetChannel
- * for a channel that is already owned by another task.
- */
-
- int
- IsThatMyChan(channel) /* returns nonzero if YOU still own the channel */
- LONG channel;
- {
- if(channel < 0 || channel > 3) return(0);
- if(usertask[channel] == FindTask(0)) return(TRUE);
- return(0);
- }
-
-
-
- /* HERE BEGINS NEW STUFF, ADDED FOR RELEASE 3.0 */
-
-
- /* PlaySamp - play a sampled sound:
- *
- * identical to PlayFreq but the parameters are interpreted differently.
- * "freq" now becomes "period" interpreted as sampling_rate,
- * must be in the range of 127 to 500.
- * "duration" still is expressed in 1000ths of a second to play it.
- * (as with the audio device itself, a duration of 0 means do it forever
- * or until the audio device is reset or the channel is flushed or
- * until this command is explicitly aborted.)
- * "length" is the number of bytes in the waveform. It is a NEW parameter
- * for this command only.
- */
- int
- PlaySamp(channel, period, wf, vol, duration, priority, messageport, id, length)
- char *wf; /* waveform to use */
- LONG vol, channel, period; /* specific integer frequency */
- LONG priority;
- LONG duration;
- struct MsgPort *messageport;
- LONG id, length;
- {
- LONG per, len; /* period, length of waveform, which octave */
- char *wavepointer; /* where to find start of waveform */
- struct ExtIOB *iob;
- long cycles, ipart;
- long dividend, divisor;
-
- if(channel < 0 || channel > 3) return(BAD_CHANNEL_SELECTED);
- printf("channel is ok\n");
- if(usertask[channel] != FindTask(0)) return(NOT_YOUR_CHANNEL);
- printf("task is ok\n");
- if(priority < -128 || priority > 127)return(OUT_OF_RANGE_PRI);
- printf("priority is ok\n");
- per = period;
-
- if(per > 500 || per < 127) return(OUT_OF_RANGE_FREQ);
- printf("period is ok\n");
- iob = GetIOB(channel);
-
- if(iob != 0)
- {
- InitBlock(iob, channel); /* set up for CMD_WRITE */
-
- wavepointer = wf;
- len = length; /* show its length;
-
- printf("length is set\n");
-
- /* Set the parameters */
- iob->ioa_Data = (UBYTE *)wavepointer;
- iob->ioa_Length = len;
- iob->ioa_Period = per;
- iob->ioa_Volume = vol;
- printf("parameters are set\n");
-
- iob->ioa_Request.io_Message.mn_Node.ln_Pri = priority;
- printf("priority is set\n");
-
- /* additions for support of tell-me-when-note-starts */
- iob->iob_Identifier = id;
- printf("id is set\n");
-
- /* see note on same line for PlayNote above */
- iob->ioa_WriteMsg.mn_ReplyPort = messageport;
-
- printf("msgport is ok\n");
-
- if(messageport != 0)
- {
- /* If 0, no sending message when note plays;
- * if nonzero, user gets a message that can
- * be read by MayGetNote. uport, received from
- * InitAudio, is what should be the value of
- * messageport.
- */
-
- /* Tell the audio device to "reply" to this message */
- iob->ioa_Request.io_Flags |= ADIOF_WRITEMESSAGE;
- }
-
- /* Calculate cycles from duration in 1000ths of a second */
- /* Multiply all-in-one to maintain max precision possible */
- /* (all integer arithmetic.) */
-
- ipart = 0;
-
- if(duration > 1000)
- {
- ipart = duration/1000;
-
- duration = duration - (ipart * 1000);
- }
-
- /* fool it a little so we dont get integer overflow...
- * 3.5 million times 1000 is about all we can take in a 32 bit word
- */
- dividend = (3579545 * ipart) + ((long)3579545 * (long)duration)/(long)1000 ;
- printf("dividend is %ld\n",dividend);
-
- divisor = len * per;
- printf("divisor is %ld\n", divisor);
-
- cycles = (long)dividend / (long)divisor;
- printf("cycles calculated to be %ld\n", cycles);
-
- iob->ioa_Cycles = cycles;
-
- if(duration != 0 && iob->ioa_Cycles == 0) iob->ioa_Cycles = 1;
- printf("cycles = %ld\n", iob->ioa_Cycles);
-
- BeginIO(iob);
- return(0); /* all went ok */
- }
- return(-1L); /* (else-part) iob value was zero, out of memory probably */
- }
-